(use-modules
 (ice-9 futures)
 (ice-9 threads)
 (ice-9 match)
 ;; SRFI 19 for time related procedures
 (srfi srfi-19))

;; Just defining a timing macro here to conveniently measure elapsed time of
;; evaluating expressions.
(define-syntax time
  (syntax-rules ()
    [(time expr expr* ...)
     (begin
       (define start-time (current-time time-monotonic))
       expr
       expr* ...
       (define end-time (current-time time-monotonic))
       (let* ([diff (time-difference end-time start-time)]
              [elapsed-ns (+ (/ (time-nanosecond diff) 1e9)
                             (time-second diff))])
         (display (format #t "~fs~%" elapsed-ns))))]))

;; Here is a prime number which needs some calculation time: 39916801

(define prime?-inefficient
  (lambda (num)
    (let loop ([divisor 2])
      (cond
       [(< divisor num)
        (if (= (remainder num divisor) 0)
            #f
            (loop (+ divisor 1)))]
       [else
        #t]))))

;; Define a procedure, which checks 2 numbers in parallel.

(define (any-prime? num1 num2)
  (let ((a-future (future (prime?-inefficient num1))))
    ;; If the first branch of or returns false, only then the other branch is
    ;; evalutated, as a shortcut behavior, so the future needs to be started
    ;; before entering the or expression.
    (or (prime?-inefficient num2)
        (touch a-future))))

(time (any-prime? 39916801 39916801))
(time (prime?-inefficient 39916801))

;; Or define a parallel map procedure as shown on:
;; https://www.gnu.org/software/guile/docs/master/guile.html/Futures.html#Futures

(define (par-map proc lst)
  (match lst
    (()
     '())
    ((head tail ...)
     (let ((tail (future (par-map proc tail)))
           (head (proc head)))
       (cons head (touch tail))))))

;; Only every <num of cores> added list element will increase runtime:
(time
 (par-map prime?-inefficient
          '(39916801
            39916801
            39916801
            39916801)))


;; Or make use of letpar to calculate let bindings in parallel:

(time
 (letpar ([res0 (prime?-inefficient 39916801)]
          [res1 (prime?-inefficient 39916801)]
          [res2 (prime?-inefficient 39916801)]
          [res3 (prime?-inefficient 39916801)])
         (and res0 res1 res2 res3)))

;; Futures are only suitable for side effect free function calls. See:
;; https://www.gnu.org/software/guile/docs/master/guile.html/Futures.html#Futures
